%%%-------------------------------------------------------------------
%%% @doc Understands the path structure of various packages types and states.  In order to keep that information encapsulated
%%% here there are a few path dependent operations in this module as well. 
%%% 
%%% Types:
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(fax_paths).

%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
-include("faxien.hrl").


%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
	 installed_release_path/3,
	 installed_release_path/2,
	 installed_release_path/1,
	 installed_app_path/3,
	 installed_app_path/1,
	 installed_erts_path/2,
	 installed_erts_path/1,
	 list_releases/1,
	 list_release_vsns/2,
	 list_apps/1,
	 list_app_vsns/2,
	 package_dir_to_name_and_vsn/1,
	 get_installation_path/0,
	 installed_config_file_path/0,
	 installed_release_file_path/3
	]).

-export([
	 init_paths/0
	]).

%%--------------------------------------------------------------------
%% Macros
%%--------------------------------------------------------------------

%%--------------------------------------------------------------------
%% Records
%%--------------------------------------------------------------------

%%====================================================================
%% External functions
%%====================================================================

%%--------------------------------------------------------------------
%% @doc Returns the full path to a rel file.
%% @spec installed_release_file_path(InstallationPath, RelName, RelVsn) -> string()
%% @end
%%--------------------------------------------------------------------
installed_release_file_path(InstallationPath, RelName, RelVsn) -> 
    lists:flatten([installed_release_file_dir(InstallationPath, RelName, RelVsn), RelName, ".rel"]).

%%--------------------------------------------------------------------
%% @doc Returns a full installaed release path. Under this directory the releases directory would sit and perhaps the bin dir.
%% @spec installed_release_path(InstallationPath, RelName, RelVsn) -> string()
%% @end
%%--------------------------------------------------------------------
installed_release_path(InstallationPath, RelName, RelVsn) when is_list(RelVsn) -> 
    lists:flatten([installed_release_path(InstallationPath, RelName), "/", RelVsn]).

installed_release_path(InstallationPath, RelName) when is_list(RelName) -> 
    lists:flatten([installed_release_path(InstallationPath), "/", RelName]).

installed_release_path(InstallationPath) when is_list(InstallationPath) -> 
    lists:flatten([InstallationPath, "/release_packages"]).

%%--------------------------------------------------------------------
%% @doc Returns a full installaed application path i.e underneith this directory lies the src and ebin dirs.
%% @spec installed_app_path(InstallationPath, AppName, AppVsn) -> string()
%% @end
%%--------------------------------------------------------------------
installed_app_path(InstallationPath, AppName, AppVsn) when is_list(AppVsn) -> 
    lists:flatten([installed_app_path(InstallationPath), "/", AppName, "-", AppVsn]).

%%--------------------------------------------------------------------
%% @doc Returns a path to where applications are to installed. Under this directory lies all installed applications
%% @spec installed_app_path(InstallationPath) -> string()
%% @end
%%--------------------------------------------------------------------
installed_app_path(InstallationPath) when is_list(InstallationPath) -> 
    lists:flatten([InstallationPath, "/lib"]).

%%--------------------------------------------------------------------
%% @doc Returns a full installaed erts path.
%% @spec installed_erts_path(InstallationPath, ErtsVsn) -> string()
%% @end
%%--------------------------------------------------------------------
installed_erts_path(InstallationPath, ErtsVsn) when is_list(ErtsVsn) -> 
    lists:flatten([installed_erts_path(InstallationPath), ErtsVsn]).

%%--------------------------------------------------------------------
%% @doc Returns a path to the directory under which all the erts packages lie.
%% @spec installed_erts_path(InstallationPath) -> string()
%% @end
%%--------------------------------------------------------------------
installed_erts_path(InstallationPath) -> 
    lists:flatten([InstallationPath, "/erts/"]).

%%--------------------------------------------------------------------
%% @doc return a list of release names that are currently installed.
%% @spec list_releases(InstallationPath) -> [string()]
%% @end
%%--------------------------------------------------------------------
list_releases(InstallationPath) -> 
    lists:map(
      fun(Path) -> filename:basename(Path) end, 
      filelib:wildcard(lists:flatten([installed_release_path(InstallationPath), "/*"]))
     ).

%%--------------------------------------------------------------------
%% @doc return a list of installed apps.
%% @spec list_apps(InstallationPath) -> [string()]
%% @end
%%--------------------------------------------------------------------
list_apps(InstallationPath) -> 
    Packages = filelib:wildcard(lists:flatten([installed_app_path(InstallationPath), "/*"])),
    AppNames = lists:map(fun(PackageName) -> 
			     {ok, {AppName, _}} = package_dir_to_name_and_vsn(PackageName),
			     AppName
		     end, Packages),
    ordsets:to_list(ordsets:from_list(AppNames)).

%%--------------------------------------------------------------------
%% @doc return a list of versions installed for a particular application.
%% @spec list_app_vsns(InstallationPath, AppName) -> [string()]
%% @end
%%--------------------------------------------------------------------
list_app_vsns(InstallationPath, AppName) -> 
    Packages = filelib:wildcard(lists:flatten([installed_app_path(InstallationPath), "/", AppName, "-*"])),
    lists:map(fun(PackageName) -> 
			     {ok, {_, Vsn}} = package_dir_to_name_and_vsn(PackageName),
			     Vsn
		     end, Packages).

%%--------------------------------------------------------------------
%% @doc return a list of versions installed for a particular release.
%% @spec list_app_vsns(InstallationPath, RelName) -> [string()]
%% @end
%%--------------------------------------------------------------------
list_release_vsns(InstallationPath, RelName) when is_atom(RelName) -> 
    list_release_vsns(InstallationPath, atom_to_list(RelName)); 
list_release_vsns(InstallationPath, RelName) -> 
    Packages = filelib:wildcard(lists:flatten([installed_release_path(InstallationPath, RelName), "/*"])),
    lists:map(fun(PackageName) -> 
			     {ok, {_, Vsn}} = package_dir_to_name_and_vsn(PackageName),
			     Vsn
		     end, Packages).

%%-------------------------------------------------------------------
%% @doc
%% returns the name and version of a package for a package directory:
%% Example:
%%  package_dir_to_name_and_vsn("/usr/local/erlware/releases/my_rel/1.2.4") -> {ok, {"my_rel". "1.2.4"}}
%%  package_dir_to_name_and_vsn("/home/martin/my_rel-1.2.4") -> {ok, {"my_rel". "1.2.4"}}
%%
%% @spec package_dir_to_name_and_vsn(RawPackageDir::string()) -> {ok, {Name, Version}} | {error, Reason}
%% @end
%%-------------------------------------------------------------------
package_dir_to_name_and_vsn(RawPackageDir) ->
    PackageDir = filename:basename(RawPackageDir),
    case regexp:match(PackageDir, "^[a-zA-Z_]+-[0-9\.-]+.*") of
	{match, 1, _} ->
	    {ok, {[PackageName], PackageVsn}} = ewl_string_manip:n_tokens(PackageDir, 1, "-"),
	    {ok, {PackageName, PackageVsn}};
	_Error -> 
	    case regexp:match(RawPackageDir, ".*/(release_packages/[a-zA-Z_]+|erts)\/[0-9\.-]+[\/]?") of
		{match, _, _} ->
		    {ok, {filename:basename(filename:dirname(RawPackageDir)), PackageDir}};
		_Error -> 
		    ?ERROR_MSG("~p is not of the form RelName/RelVsn or <relname>-<rel version>", [PackageDir]),
		    {error, "The package directory provided is not of the form RelName/RelVsn or <relname>-<rel version>"}
	    end
    end.

%%--------------------------------------------------------------------
%% @doc The installation_path variable is no longer needed in the config file except for the case of a non launcher installation.
%% In the case of usage via launcher we must rely on the -prefix argument it passes in. Config is rewritten based on that arg.
%% @spec get_installation_path() -> {ok, InstallationPath}
%% @end
%%--------------------------------------------------------------------
get_installation_path() ->
    {ok, InstallationPath} = gas:get_env(faxien, installation_path, ?INSTALLATION_PATH),
    case init:get_argument(prefix) of
	{ok, [[Prefix]]} when Prefix /= InstallationPath->
	    ?INFO_MSG("faxien:get_installation_path InstallationPath = ~p, Prefix = ~p~n", [InstallationPath, Prefix]),
	    alter_paths(Prefix),
	    {ok, Prefix};
	{ok, [[Prefix]]} ->
	    case gas:get_env(faxien, initialized, false) of
		{ok, false} ->
		    ?INFO_MSG("Initializing config~n", []),
		    alter_paths(Prefix);
		{ok, true} ->
		    ok
	    end,
	    ?INFO_MSG("faxien:get_installation_path InstallationPath = ~p, Prefix = ~p~n", [InstallationPath, Prefix]),
	    {ok, InstallationPath};
	error ->
	    ?INFO_MSG("faxien:get_installation_path InstallationPath = ~p, Prefix = ~p~n", [InstallationPath, error]),
	    {ok, InstallationPath}
    end.

%%--------------------------------------------------------------------
%% @doc return the path to config.
%% @spec installed_config_file_path() ->
%% @end
%%--------------------------------------------------------------------
installed_config_file_path() ->
    {ok, InstallationPath} = fax_paths:get_installation_path(),
    config_file_path(InstallationPath).

%%--------------------------------------------------------------------
%% @doc 
%%  Set up the config file paths based on the prefix upon bootstrapping.
%% @spec init_paths() -> ok | exit()
%% @end
%%--------------------------------------------------------------------
init_paths() ->
    {ok, [[Prefix]]} = init:get_argument(prefix),
    alter_paths(Prefix).

%%%===================================================================
%%% Internal Functions
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc Alter the config file paths based on a Prefix.
%% @end
%%--------------------------------------------------------------------
alter_paths(Prefix) ->
    gas:modify_config_file(config_file_path(Prefix), faxien, initialized, true),
    gas:modify_config_file(config_file_path(Prefix), faxien, installation_path, Prefix),
    gas:modify_config_file(config_file_path(Prefix), sasl, sasl_error_logger, {file, Prefix ++ "/log/faxien.sasl_log"}),
    gas:modify_config_file(config_file_path(Prefix), gas, err_log, Prefix ++ "/log/faxien.err_log").

%%--------------------------------------------------------------------
%% @private
%% @doc based on the installation path return the path to config
%% @end
%%--------------------------------------------------------------------
config_file_path(InstallationPath) ->
    lists:flatten([installed_release_file_dir(InstallationPath, "faxien", faxien:version()), "sys.config"]).


installed_release_file_dir(InstallationPath, RelName, RelVsn) ->
    lists:flatten([fax_paths:installed_release_path(InstallationPath, RelName, RelVsn), "/release/"]).
    
